package com.paic.common.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class IPAnalysisUtil {

	public static String randomIp() {
		Random r = new Random();
		StringBuffer str = new StringBuffer();
		str.append(r.nextInt(1000000) % 255);
		str.append(".");
		str.append(r.nextInt(1000000) % 255);
		str.append(".");
		str.append(r.nextInt(1000000) % 255);
		str.append(".");
		str.append(0);

		return str.toString();
	}

	public void test() {
		this.getClass().getClassLoader().getResource("17monipdb.dat").getFile();
	}

	public static void main(String[] args) {
		IPAnalysisUtil.load("F:\\WorkSpace\\CMS.3.0.135.tesla\\datacenter-task\\conf\\17monipdb.dat");

		// Long st = System.nanoTime();
		// for (int i = 0; i < 1000000; i++) {
		// IP.find(randomIp());
		// }
		// Long et = System.nanoTime();
		// System.out.println((et - st) / 1000 / 1000);

//		System.out.println(Arrays.toString(IPTest.find("118.28.8.8")));
	}

	public static boolean enableFileWatch = false;

	private static ReentrantLock lock = new ReentrantLock();

	//使用IPLoadResult封装load结果
	public static IPLoadResult load(String filename) {
		IPLoadResult res = _load(filename);
		if (enableFileWatch) {
			watch(res);
		}
		return res;
	}

	public static void load(String filename, IPLoadResult res, boolean strict) throws Exception {
		if (strict) {
			int contentLength = Long.valueOf(res.getIpFile().length()).intValue();
			if (contentLength < 512 * 1024) {
				throw new Exception("ip data file error.");
			}
		}
		_load(filename);
		if (enableFileWatch) {
			watch(res);
		}
	}

	public static String[] find(String ip, IPLoadResult res) {
		// Add by liaoxiaoyi,2017年4月7日
//		dataBuffer.position(0);
		
		int ip_prefix_value = new Integer(ip.substring(0, ip.indexOf(".")));
		long ip2long_value = ip2long(ip);
		int start = res.getIndex()[ip_prefix_value];
		int max_comp_len = res.getOffset() - 1028;
		long index_offset = -1;
		int index_length = -1;
		byte b = 0;
		for (start = start * 8 + 1024; start < max_comp_len; start += 8) {
			if (int2long(res.getIndexBuffer().getInt(start)) >= ip2long_value) {
				index_offset = bytesToLong(b, res.getIndexBuffer().get(start + 6),
						res.getIndexBuffer().get(start + 5), res.getIndexBuffer().get(start + 4));
				index_length = 0xFF & res.getIndexBuffer().get(start + 7);
				break;
			}
		}

		byte[] areaBytes;

		lock.lock();
		try {
			res.getDataBuffer().position(res.getOffset() + (int) index_offset - 1024);
			areaBytes = new byte[index_length];
			res.getDataBuffer().get(areaBytes, 0, index_length);
		} finally {

			lock.unlock();
		}

		return new String(areaBytes, Charset.forName("UTF-8")).split("\t", -1);
	}

	private static void watch(final IPLoadResult res) {
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				long time = res.getIpFile().lastModified();
				if (time > res.getLastModifyTime()) {
					res.setLastModifyTime(time);
					_load(res.getIpFile().getPath());
				}
			}
		}, 1000L, 5000L, TimeUnit.MILLISECONDS);
	}

	private static IPLoadResult _load(String filePath) {
//		lastModifyTime = ipFile.lastModified();
		IPLoadResult res = new IPLoadResult();
		res.setIpFile(new File(filePath));
		FileInputStream fin = null;
		lock.lock();
		try {
			res.setDataBuffer(ByteBuffer.allocate((int)res.getIpFile().length()));
			fin = new FileInputStream(res.getIpFile());
			int readBytesLength;
			byte[] chunk = new byte[4096];
			while (fin.available() > 0) {
				readBytesLength = fin.read(chunk);
				res.getDataBuffer().put(chunk, 0, readBytesLength);
			}
			res.getDataBuffer().position(0);
			int indexLength = res.getDataBuffer().getInt();
			byte[] indexBytes = new byte[indexLength];
			res.getDataBuffer().get(indexBytes, 0, indexLength - 4);
			res.setIndexBuffer(ByteBuffer.wrap(indexBytes)) ;
			res.getIndexBuffer().order(ByteOrder.LITTLE_ENDIAN);
			res.setOffset(indexLength);

			int loop = 0;
			while (loop++ < 256) {
				res.getIndex()[loop - 1] = res.getIndexBuffer().getInt();
			}
			res.getIndexBuffer().order(ByteOrder.BIG_ENDIAN);
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} finally {
			try {
				if (fin != null) {
					fin.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			lock.unlock();
		}
		
		return res;
	}

	private static long bytesToLong(byte a, byte b, byte c, byte d) {
		return int2long((((a & 0xff) << 24) | ((b & 0xff) << 16)
				| ((c & 0xff) << 8) | (d & 0xff)));
	}

	private static int str2Ip(String ip) {
		String[] ss = ip.split("\\.");
		int a, b, c, d;
		a = Integer.parseInt(ss[0]);
		b = Integer.parseInt(ss[1]);
		c = Integer.parseInt(ss[2]);
		d = Integer.parseInt(ss[3]);
		return (a << 24) | (b << 16) | (c << 8) | d;
	}

	private static long ip2long(String ip) {
		return int2long(str2Ip(ip));
	}

	private static long int2long(int i) {
		long l = i & 0x7fffffffL;
		if (i < 0) {
			l |= 0x080000000L;
		}
		return l;
	}
}
